module mux (
    input   a,
    input   b,
    input   s,
    output  y
);

assign y = s ? b : a;
    
endmodule

module inverter (
    input   a,
    output  a_inv
);

//assign a_inv = ~a;
mux u_inv(.a(1'b1), .b(1'b0), .s(a), .y(a_inv));
    
endmodule


module mux_and (
    input   a,
    input   b,
    output  y
);
    
//assign y = a & b;
mux u_and(.a(1'b0), .b(b), .s(a), .y(y));

endmodule

module mux_or (
    input   a,
    input   b,
    output  y
);
    
//assign y = a | b;
mux u_or(.a(b), .b(1'b1), .s(a), .y(y));

endmodule


module mux_nand (
    input   a,
    input   b,
    output  y
);

wire b_inv;    
//assign y = ~(a&b);
mux u_nand(.a(1'b1), .b(b_inv), .s(a), .y(y));
mux u_inv(.a(1'b1), .b(1'b0), .s(b), .y(b_inv));

endmodule

module mux_nor (
    input   a,
    input   b,
    output  y
);
    
wire b_inv;
//assign y = ~(a|b);
mux u_nor(.a(b_inv), .b(1'b0), .s(a), .y(y));
mux u_inv(.a(1'b1), .b(1'b0), .s(b), .y(b_inv));

endmodule


module mux_xor (
    input   a,
    input   b,
    output  y
);

//assign y = a^b = (a == b) ? 1'b0 : 1'b1;
//a b y
//0 0 0
//0 1 1
//1 0 1
//1 1 0
wire b_inv;
mux u_xor(.a(b), .b(b_inv), .s(a), .y(y));
mux u_inv(.a(1'b1), .b(1'b0), .s(b), .y(b_inv));
    
endmodule

module mux_xnor (
    input   a,
    input   b,
    output  y
);

wire b_inv;
//assign y = ~(a^b) = (a == b) ? 1'b1 : 1'b0;
mux u_xnor(.a(b_inv), .b(b), .s(a), .y(y));
mux u_inv(.a(1'b1), .b(1'b0), .s(b), .y(b_inv));

endmodule


module mux_tand (
    input   a,
    input   b,
    input   c,
    output  y
);
    
wire b_and_c;
//assign y = a&b&c;
mux u_tand(.a(1'b0), .b(b_and_c), .s(a), .y(y));
mux u_and(.a(1'b0), .b(c), .s(b), .y(b_and_c));

endmodule

module mux_tor (
    input   a,
    input   b,
    input   c,
    output  y
);

wire b_or_c; 
//assign y = a|b|c;
mux u_tor(.a(b_or_c), .b(1'b1), .s(a), .y(y));
mux u_or(.a(c), .b(1'b1), .s(b), .y(b_or_c));

endmodule


module mux_tnand (
    input   a,
    input   b,
    input   c,
    output  y
);
    
wire b_and_c, inv_mid;
//assign y = ~(a&b&c) = (~a) | (~(b&c));
mux u_tnand(.a(1'b1), .b(inv_mid), .s(a), .y(y));
mux u_inv(.a(1'b1), .b(1'b0), .s(b_and_c), .y(inv_mid));
mux u_and(.a(1'b0), .b(c), .s(b), .y(b_and_c));

endmodule

module mux_tnor (
    input   a,
    input   b,
    input   c,
    output  y
);
    
wire b_or_c, inv_mid;
//assign y = ~(a|b|c) = (~a) & (~(b|c));
mux u_tnor(.a(inv_mid), .b(1'b0), .s(a), .y(y));
mux u_inv(.a(1'b1), .b(1'b0), .s(b_or_c), .y(inv_mid));
mux u_or(.a(c), .b(1'b1), .s(b), .y(b_or_c));

endmodule


module mux_txor (
    input   a,
    input   b,
    input   c,
    output  y
);
    
//assign y = a^b^c = a ? ~(b^c) : b^c = a ? (b ? c : ~c) : (b ? ~c : c)
/*  a b c y
    0 0 0 0
    0 0 1 1
    0 1 0 1
    0 1 1 0
    1 0 0 1
    1 0 1 0
    1 1 0 0
    1 1 1 1
*/
wire c_inv, b_xor_c, inv_mid;
mux u_txor(.a(b_xor_c), .b(inv_mid), .s(a), .y(y));
// mux u_xnor(.a(1'b1), .b(1'b0), .s(b_xor_c), .y(inv_mid));
mux u_xnor(.a(c_inv), .b(c), .s(b), .y(inv_mid));
mux u_xor(.a(c), .b(c_inv), .s(b), .y(b_xor_c));
mux u_inv(.a(1'b1), .b(1'b0), .s(c), .y(c_inv));

endmodule

module mux_txnor (
    input   a,
    input   b,
    input   c,
    output  y
);
    
//assign y = ~(a^b^c) = a ? b^c : ~(b^c) = a ? (b ? ~c : c) : (b ? c : ~c)
wire c_inv, b_xor_c, inv_mid;
mux u_txnor(.a(inv_mid), .b(b_xor_c), .s(a), .y(y));
// mux u_xnor(.a(1'b1), .b(1'b0), .s(b_xor_c), .y(inv_mid));
mux u_xnor(.a(c_inv), .b(c), .s(b), .y(inv_mid));
mux u_xor(.a(c), .b(c_inv), .s(b), .y(b_xor_c));
mux u_inv(.a(1'b1), .b(1'b0), .s(c), .y(c_inv));

endmodule


module mux_add_half (
    input   a,
    input   b,
    output  sum,
    output  cout
);
    
//assign sum = a^b  cout = a&b
wire b_inv;
mux u_sum(.a(b), .b(b_inv), .s(a), .y(sum));
mux u_cout(.a(1'b0), .b(b), .s(a), .y(cout));

mux u_inv(.a(1'b1), .b(1'b0), .s(b), .y(b_inv));

endmodule

module mux_add_full (
    input   a,
    input   b,
    input   cin,
    output  sum,
    output  cout
);
    
//sum = a^b^cin = a ? ~(b^cin) : b^cin = a ? (b ? cin : ~cin) : (b ? ~cin : cin)
//cout = a&b | b&cin | a&cin = a ? b|cin : b&cin = a ? (b ? 1'b1 : cin) : (b ? cin : 1'b0)
wire cin_inv, b_xor_cin, b_xor_cin_inv, b_or_cin, b_and_cin;

mux u_sum(.a(b_xor_cin), .b(b_xor_cin_inv), .s(a), .y(sum));
mux u_cout(.a(b_and_cin), .b(b_or_cin), .s(a), .y(cout));

mux u_or(.a(cin), .b(1'b1), .s(b), .y(b_or_cin));
mux u_and(.a(1'b0), .b(cin), .s(b), .y(b_and_cin));
mux u_xnor(.a(cin_inv), .b(cin), .s(b), .y(b_xor_cin_inv));
mux u_xor(.a(cin), .b(cin_inv), .s(b), .y(b_xor_cin));
mux u_inv(.a(1'b1), .b(1'b0), .s(cin), .y(cin_inv));

endmodule

//not safe in engineering, only for study
module mux_latch_pos (
    input   d,
    input   en,
    output  q
);

//q = en ? d : q

mux u_latch(.a(q), .b(d), .s(en), .y(q));

endmodule
//not safe in engineering, only for study
module mux_latch_neg (
    input   d,
    input   en,
    output  q
);

//q = en ? q : d
wire q_d, q_inv;
mux u_latch(.a(d), .b(q), .s(en), .y(q));
    
endmodule

//not safe in engineering, only for study
module mux_dff_pos (
    input clk,
    input d,
    output q
);
    
wire qin;
mux u1_latch(.a(q), .b(qin), .s(clk), .y(q));
mux u2_latch(.a(qin), .b(d), .s(clk_inv), .y(qin));

endmodule
//not safe in engineering, only for study
module mux_dff_neg (
    input clk,
    input d,
    output q
);
    
wire qin;
mux u1_latch(.a(qin), .b(q), .s(clk), .y(q));
mux u2_latch(.a(d), .b(qin), .s(clk_inv), .y(qin));

endmodule